package com.lazydong.apiforward.clientupdate.gui;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.lazydong.apiforward.clientupdate.constant.UpdateConstant;
import com.lazydong.apiforward.clientupdate.util.OpenExeUtil;
import com.lazydong.gui.layout.Margin;
import com.lazydong.gui.layout.Size;
import com.lazydong.gui.layout.linear.HLinearAlign;
import com.lazydong.gui.layout.linear.LinearLayoutUtil;
import com.lazydong.gui.layout.linear.VLinearAlign;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;

public class UpdateMainApp {
    private static final int windowWidth = 400;
    private static final int windowHeight = 200;
    private static final JButton button = new JButton();
    private static final JTextField tf = new JTextField();
    private static final JLabel tip = new JLabel();

    public void run() {
        // 初始化窗口
        JFrame jFrame = new JFrame();
        jFrame.setTitle("更新辅助程序");
        // 设置图标
//        ImageIcon imageIcon = new ImageIcon();
//        jFrame.setIconImage(imageIcon.getImage());
        // 设置窗口的位置和大小
        jFrame.setBounds(400, 300, windowWidth, windowHeight);
        Container contentPane = jFrame.getContentPane();
        // 中心面板大小大概比 jFrame（除去标题等元素外） 的大小要小 （宽20:高40）试出来的
        contentPane.setSize(windowWidth - 20, windowHeight - 40);
        addContentPaneLayout(contentPane);
        jFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("点击了关闭按钮");
            }
        });
        // 设置窗口是否可见
        jFrame.setVisible(true);
        // 设置窗口是否可以缩放
        jFrame.setResizable(false);
        /*
         * 设置窗口的相对位置。
         * 如果 comp 整个显示区域在屏幕内, 则将窗口放置到 comp 的中心;
         * 如果 comp 显示区域有部分不在屏幕内, 则将该窗口放置在最接近 comp 中心的一侧;
         * comp 为 null, 表示将窗口放置到屏幕中心。
         */
        jFrame.setLocationRelativeTo(null);
        /*
         * 设置窗口关闭按钮点击后的默认操作, 参考值:
         *     WindowConstants.DO_NOTHING_ON_CLOSE: 不执行任何操作。
         *     WindowConstants.HIDE_ON_CLOSE: 隐藏窗口（不会结束进程）, 再次调用 setVisible(true) 将再次显示。
         *     WindowConstants.DISPOSE_ON_CLOSE: 销毁窗口, 如果所有可显示的窗口都被 DISPOSE, 则可能会自动结束进程。
         *     WindowConstants.EXIT_ON_CLOSE: 退出进程。
         */
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.setLayout(null);
    }

    private void addContentPaneLayout(Container contentPane) {
        LinearLayoutUtil.setVLayout(contentPane, VLinearAlign.Centre);
        JLabel onePanel = new JLabel("请输入服务器地址，点击更新按钮开始更新最新版本");
        LinearLayoutUtil.addComponent(contentPane, onePanel, Size.ofRatio(80, 15), Margin.ofTopRatio(5));

        JPanel two = new JPanel();
        LinearLayoutUtil.setHLayout(two, HLinearAlign.Centre);
        two.setBackground(Color.MAGENTA);
        LinearLayoutUtil.addComponent(contentPane, two, Size.ofRatio(80, 15), Margin.ofTopRatio(5));
        JLabel desc = new JLabel("服务器地址:");
        LinearLayoutUtil.addComponent(two, desc, Size.ofRatio(25, 100));

        // 设置服务器默认值
        if (FileUtil.exist(UpdateConstant.SERVER_IP_ADDRESS)) {
            String address = FileUtil.readString(UpdateConstant.SERVER_IP_ADDRESS, StandardCharsets.UTF_8);
            tf.setText(address);
        }
        LinearLayoutUtil.addComponent(two, tf, Size.ofWidthRatio(80));

        // 提示信息栏
        JPanel three = new JPanel();
        LinearLayoutUtil.addComponent(contentPane, three, Size.ofRatio(70, 15), Margin.ofTopRatio(5));
        LinearLayoutUtil.setHLayout(three, HLinearAlign.Centre);
        LinearLayoutUtil.addComponent(three, tip, Size.ofWidthRatio(100));

        // 按钮
        UpdateButtonListener updateButtonListener = new UpdateButtonListener();
        button.setActionCommand("update");
        button.addActionListener(updateButtonListener);
        button.setText("更新");
        LinearLayoutUtil.addComponent(contentPane, button, Size.ofRatio(40, 20), Margin.ofTopRatio(5));
    }

    private static void downZipFile() {
        tip.setText("正在下载中...");
        button.setEnabled(false);
        tf.setEnabled(false);
        String text = tf.getText();
        String url = text + UpdateConstant.DOWNLOAD_PATH;
        BufferedOutputStream outputStream = FileUtil.getOutputStream(UpdateConstant.CLIENT_ZIP_PATH);
        try {
            HttpUtil.download(url, outputStream, true, new DownStreamProgress());
        } catch (Exception e) {
            tip.setText("下载失败，请输入正确的服务器再点击更新");
            button.setEnabled(true);
            tf.setEnabled(true);
            return;
        }
        // 说明地址有效,保存起来
        FileUtil.writeString(text, UpdateConstant.SERVER_IP_ADDRESS, StandardCharsets.UTF_8);
        tip.setText("解压中...");
        ZipUtil.unzip(UpdateConstant.CLIENT_ZIP_PATH, UpdateConstant.CLIENT_DIR, StandardCharsets.UTF_8);
        tip.setText("更新完成，点击重启按钮立即使用");
        button.setEnabled(true);
        button.setText("重启");
        button.setActionCommand("reStart");
        // 删除 更新程序时最新的 标识，下次检测到最新版本时，会重新下载 更新程序
        for (File file : FileUtil.ls(UpdateConstant.CLIENT_UPDATE_DIR)) {
            String name = file.getName();
            if (name.endsWith(UpdateConstant.UPDATE_LOCK_SUFFIX)) {
                FileUtil.del(file);
            }
        }
        // 客户端检测到这个文件，会把update文件夹整体删掉
        FileUtil.touch(UpdateConstant.UPDATE_SUCCESS_FLAG);
    }

    static class UpdateButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String actionCommand = e.getActionCommand();
            if ("update".equals(actionCommand)) {
                ThreadUtil.execAsync(UpdateMainApp::downZipFile);
                return;
            }
            if ("reStart".equals(actionCommand)) {
                ThreadUtil.execAsync(OpenExeUtil::openClientExe);
            }
        }
    }

    static class DownStreamProgress implements StreamProgress {
        BigDecimal multiplyDecimal = BigDecimal.valueOf(100);

        @Override
        public void start() {
            tip.setText("正在下载中...");
        }

        @Override
        public void progress(long total, long progressSize) {
            if (total == Long.MAX_VALUE || total == -1) {
                return;
            }
            BigDecimal progressSizeDecimal = BigDecimal.valueOf(progressSize);
            BigDecimal totalDecimal = BigDecimal.valueOf(total);
            BigDecimal divide = progressSizeDecimal.multiply(multiplyDecimal).divide(totalDecimal, 2, RoundingMode.HALF_UP);
            tip.setText("正在下载中...当前进度 " + divide + "%");
        }

        @Override
        public void finish() {
            tip.setText("下载完成");
        }
    }
}
